﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace DbSharp.ConsoleRunner.Parser
{
    public class SwitchArgument : Argument
    {
        #region property backing fieldds

        private bool value;

        private bool defaultValue;

        #endregion

        #region constructors

        /// <summary>
        /// Creates new switch argument with a <see cref="Argument.ShortName">short name</see>.
        /// </summary>
        /// <param name="shortName">Short name of the argument</param>
        /// <param name="defaultValue">default value of the argument</param>
        public SwitchArgument(char shortName, bool defaultValue)
            : base(shortName)
        {
            this.defaultValue = defaultValue;
            this.value = defaultValue;
        }

        /// <summary>
        /// Creates new switch argument with a <see cref="Argument.ShortName">short name</see>and <see cref="Argument.LongName">long name</see>.
        /// </summary>
        /// <param name="shortName">Short name of the argument</param>
        /// <param name="longName">Long name of the argument </param>
        /// <param name="defaultValue">default value of the argument</param>
        public SwitchArgument(char shortName, string longName, bool defaultValue)
            : base(shortName, longName)
        {
            this.defaultValue = defaultValue;
            this.value = defaultValue;
        }

        /// <summary>
        /// Creates new switch argument with a <see cref="Argument.ShortName">short name</see>,
        /// <see cref="Argument.LongName">long name</see> and <see cref="Argument.Description">description</see>
        /// </summary>
        /// <param name="shortName">Short name of the argument</param>
        /// <param name="longName">Long name of the argument </param>
        /// <param name="description">description of the argument</param>
        /// <param name="defaultValue">default value of the argument</param>
        public SwitchArgument(char shortName, string longName, string description, bool defaultValue)
            : base(shortName, longName, description)
        {
            this.defaultValue = defaultValue;
            this.value = defaultValue;
        }

        #endregion

        /// <summary>
        /// Value of the switch argument
        /// </summary>
        public bool Value
        {
            get { return value; }
            set { this.value = value; }
        }

        /// <summary>
        /// Default value of the switch argument. Restored each time <see cref="Init"/> is called.
        /// </summary>
        public bool DefaultValue
        {
            get { return defaultValue; }
            set { defaultValue = value; }
        }


        /// <summary>
        /// Parse argument. This method reads the argument from the input field and moves the 
        /// index to the next argument.
        /// </summary>
        /// <param name="args">command line arguments</param>
        /// <param name="i">index to the args array, where this argument occured. </param>
        internal override void Parse(IList<string> args, ref int i)
        {
            base.Parse(args, ref i);
            value = !value;
            Parsed = true;
            i++;
        }

        /// <summary>
        /// If <see cref="Argument.Bind"/> is specified, the bound field of the bound object should is updated
        /// according to the value of the argument. Should be called after Parse is called.  
        /// </summary>
        public override void UpdateBoundObject()
        {
            if (Bind.HasValue)
            {
                try
                {
                    Bind.Value.Object.GetType().InvokeMember(Bind.Value.Field,
                                                             BindingFlags.SetField | BindingFlags.SetProperty,
                                                             null, Bind.Value.Object, new object[] { value });
                }
                catch (Exception e)
                {
                    throw new CommandLineException(
                        string.Format(SR.EXC_BINDING, Name,
                                      Bind.Value.Field, Bind.Value.Object), e);
                }
            }
        }

        /// <summary>
        /// Prints information about the argument value to the console.
        /// </summary>
        internal override void PrintValueInfo()
        {
            Console.WriteLine(SR.EXC_ARG_SWITCH_PRINT, Name, value == true ? "1" : "0");
        }

        /// <summary>
        /// Initializes the argument and restores the <see cref="DefaultValue"/>.
        /// </summary>
        public override void Init()
        {
            base.Init();
            this.value = defaultValue;
        }
    }
}
